package com.cjm.one.goods.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.cjm.one.common.exception.GlobalException;
import com.cjm.one.common.result.ResultCodeEnum;
import com.cjm.one.goods.mapper.CouponInfoMapper;
import com.cjm.one.goods.mapper.CustomerCouponMapper;
import com.cjm.one.goods.mapper.GoodsSkuMapper;
import com.cjm.one.goods.service.CouponCalculationService;
import com.cjm.one.goods.service.CouponRangeService;
import com.cjm.one.goods.service.CustomerCouponService;
import com.cjm.one.goods.service.GoodsSkuService;
import com.cjm.one.model.coupon.context.CouponContext;
import com.cjm.one.model.coupon.dto.CartItemDTO;
import com.cjm.one.model.coupon.dto.OrderCalculateDTO;
import com.cjm.one.model.coupon.entity.CouponInfo;
import com.cjm.one.model.coupon.entity.CustomerCoupon;
import com.cjm.one.model.coupon.vo.CouponInfoVo;
import com.cjm.one.model.coupon.vo.GoodsDiscountVO;
import com.cjm.one.model.goods.entity.GoodsSku;
import com.cjm.one.model.goods.vo.SkuWithQuantity;
import com.cjm.one.strategy.CouponStrategy;
import com.cjm.one.model.coupon.entity.CouponRange;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Slf4j
public class CouponCalculationServiceImpl
        extends ServiceImpl<CouponInfoMapper, CouponInfo>
        implements CouponCalculationService {

    @Resource
    private CustomerCouponMapper customerCouponMapper;

    @Resource
    private  CouponInfoMapper couponInfoMapper;
    @Resource
    private GoodsSkuMapper goodsSkuMapper;
    /**
     * @description: 计算最优优惠策略
     * @author: cjm 
     * @date: 2025/5/8 23:50
     * @param: [dto]
     * @return: java.util.List<com.cjm.one.model.coupon.vo.GoodsDiscountVO>
     **/
    @Cacheable(
            value = "couponStrategies",
            unless = "#result == null || #result.isEmpty()"
    )
    @Override
    public List<GoodsDiscountVO> calculateOptimalDiscount(OrderCalculateDTO dto) {
        validateCalculation(dto);
        // 1. 获取商品基本信息
        log.info("获取商品基本信息dto:{}", dto);
        List<SkuWithQuantity> skuList = getSkuDetails(dto.getCartItems());

        // 2. 获取所有可用优惠券（包含用户未领取但可用的）
        List<CouponInfoVo> allCoupons = getAllAvailableCoupons(dto.getUserId());

        log.info("allCoupons:{}", allCoupons);
        // 3. 构建优惠计算上下文
        CouponContext context = buildCouponContext(skuList, allCoupons);

        // 4. 执行优惠计算策略
        return new CouponStrategy().calculate(context);
    }

    private List<SkuWithQuantity> getSkuDetails(List<CartItemDTO> items) {
        if (items == null || items.isEmpty()) {
            return Collections.emptyList();
        }
        Map<Long, Integer> quantityMap = items.stream()
                .collect(Collectors.toMap(CartItemDTO::getSkuId, CartItemDTO::getQuantity));

        List<GoodsSku> skus = goodsSkuMapper.selectBatchIds(quantityMap.keySet());
        log.info("skus:{}", skus);
        return skus.stream()
                .map(sku -> new SkuWithQuantity(sku, quantityMap.get(sku.getId())))
                .collect(Collectors.toList());

    }

    private List<CouponInfoVo> getAllAvailableCoupons(Long userId) {
        //获取用户已经使用了的 优惠券
        List<CustomerCoupon> validCoupons = customerCouponMapper.findValidCoupons(userId);

        // 获取系统所有有效优惠券
        List<CouponInfoVo> couponsByRange = Optional.ofNullable(couponInfoMapper.findCouponsByRange())
                .orElse(Collections.emptyList());

        return mergeCoupons(validCoupons, couponsByRange);

    }

    private List<CouponInfoVo> mergeCoupons(List<CustomerCoupon> userCoupons,
                                          List<CouponInfoVo> systemCoupons) {
        // 1. 收集用户已经使用了的优惠卷ID到Set集合中
//        TODO: 此处还存在问题，优惠卷可使用多次时
        Set<Long> userCouponIds = userCoupons.stream()
                .map(CustomerCoupon::getCouponId)
                .collect(Collectors.toSet());
        // 2. 过滤系统优惠券，保留用户没有使用过的优惠券ID列表
        return Optional.ofNullable(systemCoupons)
                .orElse(Collections.emptyList())
                .stream()
                .filter(vo -> !userCouponIds.contains(vo.getId()))
        .collect(Collectors.toList());

    }

    private CouponContext buildCouponContext(List<SkuWithQuantity> skuList, List<CouponInfoVo> allCoupons) {
        CouponContext context = new CouponContext();
        context.setSkuWithQuantities(skuList);
        List<GoodsSku> goodsSkuList = skuList.stream().map(SkuWithQuantity::getSku).collect(Collectors.toList());
        // 按类型分组
        Map<Integer, List<CouponInfoVo>> couponGroups = allCoupons.stream()
                .collect(Collectors.groupingBy(CouponInfoVo::getRangeType));

        // 构建关联映射（商品券需匹配SKU，分类券需匹配分类）
        context.setItemCoupons(filterItemCoupons(goodsSkuList, couponGroups.getOrDefault(3, Collections.emptyList()) ));
        context.setCategoryCoupons(filterCategoryCoupons(goodsSkuList,  couponGroups.getOrDefault(2, Collections.emptyList())));
        context.setUniversalCoupons(couponGroups.getOrDefault(1, Collections.emptyList()));

        return context;
    }
    // 商品券过滤（匹配SKU）
    private List<CouponInfoVo> filterItemCoupons(List<GoodsSku> skus, List<CouponInfoVo> coupons) {
        Set<Long> skuIds = skus.stream()
                .map(GoodsSku::getGoodsId)
                .collect(Collectors.toSet());
        if (coupons == null || coupons.isEmpty()) {
            return Collections.emptyList();
        }
        return coupons.stream().filter(coupon -> {
                    Integer rangeType = coupon.getRangeType();
                    if (rangeType == null) return false;
                    if (rangeType == 3) {
                        List<CouponRange> ranges = coupon.getRangeList();

                        return ranges != null && ranges.stream()
                                .anyMatch(range ->
                                        range.getRangeId() != null &&
                                                skuIds.contains(range.getRangeId())
                                );
                    }
                    return false;
                }
                ).collect(Collectors.toList());
    }

    // 分类券过滤（匹配商品分类）
    private List<CouponInfoVo> filterCategoryCoupons(List<GoodsSku> skus, List<CouponInfoVo> coupons) {
        if (CollectionUtils.isEmpty(coupons)) {
            return Collections.emptyList();
        }
        Set<Long> categoryIds = skus.stream()
                .flatMap(s -> Stream.of(s.getCategory1Id(), s.getCategory2Id()))
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        return coupons.stream().filter(coupon -> {
                    Integer rangeType = coupon.getRangeType();
                    if (rangeType == 2) {
                        List<CouponRange> ranges = coupon.getRangeList();
                        return ranges != null && ranges.stream()
                                .anyMatch(range ->
                                        range.getRangeId() != null &&
                                                categoryIds.contains(range.getRangeId())
                                );
                    }
                    return false;
                }
        ).collect(Collectors.toList());
    }

    private void validateCalculation(OrderCalculateDTO dto) {
        if (dto == null) {
            throw new GlobalException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
        }
        if (dto.getCartItems().isEmpty()) {
            throw new GlobalException(233,"购物车不能为空");
        }


    }

}
